home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue61 / System / FTPReader.pas next >
Encoding:
Pascal/Delphi Source File  |  2000-08-07  |  7.2 KB  |  213 lines

  1. unit FTPReader;
  2.  
  3. interface
  4.  
  5. uses Windows, WinINet, SysUtils, Classes, Dialogs;
  6.  
  7. const
  8.     BufSize = $1000;
  9.  
  10. type
  11.     TFTPFileReader = class;
  12.  
  13.     TFTPFileReaderThread = class (TThread)
  14.     private
  15.         Owner: TFTPFileReader;
  16.         procedure DoProgress;
  17.     public
  18.         procedure Execute; override;
  19.     end;
  20.  
  21.     TFTPFileReader = class (TObject)
  22.     private
  23.         fNetConnection: HInternet;
  24.         fFTPSession: HInternet;
  25.         fFileHandle: HInternet;
  26.         fSourceFileName: String;
  27.         fServerName: String;
  28.         fDestStream: TFileStream;
  29.         fDestFileName: String;
  30.         fUserName: String;
  31.         fPassword: String;
  32.         fFileSize: Integer;
  33.         fOwnFTPSession: Boolean;
  34.         fServerPort: Integer;
  35.         fTotalBytesRead: Integer;
  36.         fOnProgress: TNotifyEvent;
  37.         fCompletionString: String;
  38.         fOnCompletetion: TNotifyEvent;
  39.         fThread: TFTPFileReaderThread;
  40.         fBuffer: array [0..BufSize - 1] of Char;
  41.         procedure Cleanup (Fail: Boolean);
  42.         procedure Panic (const Message: String);
  43.         function StartSession: Boolean;
  44.         procedure ThreadTerminated (Sender: TObject);
  45.     public
  46.         destructor Destroy; override;
  47.         procedure Execute;
  48.         procedure CancelTransfer;
  49.         property FileSize: Integer read fFileSize;
  50.         property TotalBytesRead: Integer read fTotalBytesRead;
  51.         property CompletionString: String read fCompletionString;
  52.         property NetConnection: HInternet read fNetConnection write fNetConnection;
  53.         property FTPSession: HInternet read fFTPSession write fFTPSession;
  54.         property SourceFileName: String read fSourceFileName write fSourceFileName;
  55.         property DestFileName: String read fDestFileName write fDestFileName;
  56.         property ServerName: String read fServerName write fServerName;
  57.         property ServerPort: Integer read fServerPort write fServerPort;
  58.         property OnProgress: TNotifyEvent read fOnProgress write fOnProgress;
  59.         property OnCompletion: TNotifyEvent read fOnCompletetion write fOnCompletetion;
  60.     end;
  61.  
  62. implementation
  63.  
  64. // TFTPFileReader
  65.  
  66. destructor TFTPFileReader.Destroy;
  67. begin
  68.     Cleanup (False);
  69.     Inherited Destroy;
  70. end;
  71.  
  72. procedure TFTPFileReader.Cleanup (Fail: Boolean);
  73. begin
  74.     // Close the destination file stream if open;
  75.     fDestStream.Free;
  76.     fDestStream := Nil;
  77.     if Fail then DeleteFile (fDestFileName);
  78.  
  79.     // Close the source file if its open
  80.     if fFileHandle <> Nil then begin
  81.         InternetCloseHandle (fFileHandle);
  82.         fFileHandle := Nil;
  83.     end;
  84.  
  85.     // Tear down the FTP session, if any
  86.     if fOwnFTPSession and (fFTPSession <> Nil) then begin
  87.         InternetCloseHandle (fFTPSession);
  88.         fFTPSession := Nil;
  89.         fOwnFTPSession := False;
  90.     end;
  91. end;
  92.  
  93. procedure TFTPFileReader.Panic (const Message: String);
  94. begin
  95.     Cleanup (True);
  96.     raise Exception.Create (ClassName + ': ' + Message);
  97. end;
  98.  
  99. procedure TFTPFileReader.CancelTransfer;
  100. begin
  101.     Cleanup (True);
  102. end;
  103.  
  104. function TFTPFileReader.StartSession: Boolean;
  105. var
  106.     FindHandle: HInternet;
  107.     FindData: TWin32FindData;
  108.     szUserName, szPassword: PChar;
  109. begin
  110.     // Do we need to create an FTP session ?
  111.  
  112.     if fFTPSession = Nil then begin
  113.         if fNetConnection = Nil then Panic ('No Network connection specified');
  114.         if (fUserName = '') or (fPassword = '') then begin
  115.             szUserName := Nil;  szPassword := Nil;
  116.         end else begin
  117.             szUserName := @fUserName [1];  szPassword := @fPassword [1];
  118.         end;
  119.  
  120.         fFTPSession := InternetConnect (fNetConnection, PChar (fServerName), fServerPort,
  121.                                         szUserName, szPassword, Internet_Service_FTP, 0, 0);
  122.         if fFTPSession = Nil then Panic ('Can''t create an FTP session');
  123.         fOwnFTPSession := True;
  124.     end;
  125.  
  126.     // We've got an FTP session.  How big is the file?
  127.     FindHandle := FtpFindFirstFile (fFTPSession, PChar (fSourceFileName), FindData, 0, 0);
  128.     if FindHandle <> Nil then begin
  129.         fFileSize := FindData.nFileSizeLow;
  130.         InternetCloseHandle (FindHandle);
  131.     end;
  132.  
  133.     // Now, try and open the file for transfer
  134.     fFileHandle := FtpOpenFile (fFTPSession, PChar (fSourceFileName), Generic_Read, Ftp_Transfer_Type_Binary, 0);
  135.     Result := fFileHandle <> Nil;
  136. end;
  137.  
  138. procedure TFTPFileReader.Execute;
  139. begin
  140.     // Perform all needed sanity checks....
  141.     if fServerName = '' then Panic ('No server name specified');
  142.     if fSourceFileName = '' then Panic ('No source filename specified');
  143.     if fDestFileName = '' then Panic ('No destination filename specified');
  144.     if fServerPort = 0 then fServerPort := Internet_Default_FTP_Port;
  145.     // So far so good....now create an FTP session
  146.     if not StartSession then Panic ('Requested file not found') else begin
  147.         // Thunderbirds are go!  It's time to create the background thread,
  148.         // create the source file and start rolling....
  149.         try
  150.             fDestStream := TFileStream.Create (fDestFileName, fmCreate);
  151.         except
  152.             Panic ('Can''t create destination file');
  153.         end;
  154.  
  155.         fThread := TFTPFileReaderThread.Create (True);
  156.         fThread.FreeOnTerminate := True;
  157.         fThread.OnTerminate := ThreadTerminated;
  158.         fThread.Owner := Self;
  159.         fThread.Resume;
  160.     end;
  161. end;
  162.  
  163. procedure TFTPFileReader.ThreadTerminated (Sender: TObject);
  164. begin
  165.     if Assigned (OnCompletion) then OnCompletion (Self);
  166. end;
  167.  
  168. // TFTPFileReaderThread
  169.  
  170. procedure TFTPFileReaderThread.DoProgress;
  171. begin
  172.     if Assigned (Owner.OnProgress) then Owner.OnProgress (Owner);
  173. end;
  174.  
  175. procedure TFTPFileReaderThread.Execute;
  176. var
  177.     BytesRead: DWord;
  178.     ErrNum, BuffSize: DWord;
  179.     szBuff: array [0..1024] of Char;
  180. begin
  181.     while not Terminated do begin
  182.         BytesRead := 0;
  183.         if InternetReadFile (Owner.fFileHandle, @Owner.fBuffer, sizeof (Owner.fBuffer), BytesRead) then begin
  184.             // If we've got some more data, write it to destination file
  185.             if (BytesRead > 0) and (BytesRead <= sizeof (Owner.fBuffer)) then begin
  186.                 Owner.fDestStream.Write (Owner.fBuffer, BytesRead);
  187.                 // Update progress info......
  188.                 Owner.fTotalBytesRead := Owner.fTotalBytesRead + Integer (BytesRead);
  189.                 Synchronize (DoProgress);
  190.             end;
  191.  
  192.             // If we didn't get any data, but InternetReadFile returned True, then it's EOF
  193.             // Just terminate the thread by leaving Execute.
  194.             if BytesRead = 0 then begin
  195.                 Owner.fCompletionString := 'OK';
  196.                 Exit;
  197.             end;
  198.         end else begin
  199.             // It looks bad.  InternetReadFile has returned False which basically means its
  200.             // screwed up.  Get the last response string from the server and pass it back.
  201.             BuffSize := sizeof (szBuff);  Owner.fCompletionString := 'Unknown Error';
  202.             if InternetGetLastResponseInfo (ErrNum, szBuff, BuffSize) then
  203.                 if (BuffSize > 0) and (szBuff [0] <> #0) then
  204.                     Owner.fCompletionString := szBuff;
  205.             // We're out'a here......
  206.             Exit;
  207.         end;
  208.     end;
  209. end;
  210.  
  211. end.
  212.  
  213.